'''
=========================
!/usr/bin/env python
_*_ coding:utf-8 _*_

@Time : 2020/12/31 9:57
@Coder: xh
@IDE  : PyCharm
=========================
'''
import logging
logging.basicConfig(level=logging.INFO)  # 设置日志显示等级
import asyncio
import json
import os
import time
from datetime import datetime
from aiohttp import web  # aiohttp是基于asyncio实现的HTTP框架

# 处理函数
#
# def index(request):  # 函数名自取，参数为aiohttp.web.request实例，包含了所有浏览器发送过来的HTTP协议里面的信息，一般不用自己构造
#     return web.Response(
#         body='<h1>This is a test statement!</h1>',
#         content_type='text/html')  # 构造一个HTTP响应

# # @asyncio.coroutine把一个generator标记为coroutine类型，然后把这个coroutine放到EventLoop中执行。
# @asyncio.coroutine
# def init(loop):
#     app = web.Application(loop=loop)  # 创建web服务器实例，作用是处理url、http协议
#     # 将URL注册到router，将处理函数注册到router，并将处理函数和URL(HTTP方法method，URL路径path)绑定，浏览器请求URL时返回处理函数的内容
#     app.router.add_route('GET', '/', index)
#     # 用协程创建监听服务
#     srv = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9000)
#     # make_handler创建aiohttp.RequestHandlerFactory,用来处理HTTP协议
#     # yield from 返回一个创建好的，绑定IP、端口、HTTP协议簇的监听服务的协程。yield from的作用是使srv的行为模式和
#     # loop.create_server()一致
#     logging.info(r'服务器启动于 http://127.0.0.1:9000')
#     return srv

from jinja2 import Environment, FileSystemLoader
from config import configs

import orm
from coroweb import add_routes, add_static
from handlers import cookie2user, COOKIE_NAME

def init_jinja2(app, **kw):
    logging.info('init jinja2...')
    options = dict(
        autoescape=kw.get('autoescape', True),
        block_start_string=kw.get('block_start_string', '{%'),
        block_end_string=kw.get('block_end_string', '%}'),
        variable_start_string=kw.get('variable_start_string', '{{'),
        variable_end_string=kw.get('variable_end_string', '}}'),
        auto_reload=kw.get('auto_reload', True)
    )
    path = kw.get('path', None)
    if path is None:
        path = os.path.join(
            os.path.dirname(
                os.path.abspath(__file__)),
            'templates')
    logging.info('set jinja2 template path: %s' % path)
    env = Environment(loader=FileSystemLoader(path), **options)
    filters = kw.get('filters', None)
    if filters is not None:
        for name, f in filters.items():
            env.filters[name] = f
    app['__templating__'] = env

async def logger_factory(app, handler):
    async def logger(request):
        logging.info('Request: %s %s' % (request.method, request.path))
        # await asyncio.sleep(0.3)
        return (await handler(request))
    return logger


async def auth_factory(app, handler):
    async def auth(request):
        logging.info('check user: %s %s' % (request.method, request.path))
        request.__user__ = None
        cookie_str = request.cookies.get(COOKIE_NAME)
        if cookie_str:
            user = await cookie2user(cookie_str)
            if user:
                logging.info('set current user: %s' % user.email)
                request.__user__ = user
        if request.path.startswith('/manage/') and (request.__user__ is None or not request.__user__.admin):
            return web.HTTPFound('/login')
        return (await handler(request))
    return auth

async def data_factory(app, handler):
    async def parse_data(request):
        if request.method == 'POST':
            if request.content_type.startswith('application/json'):
                request.__data__ = await request.json()
                logging.info('request json: %s' % str(request.__data__))
            elif request.content_type.startswith('application/x-www-form-urlencoded'):
                request.__data__ = await request.post()
                logging.info('request form: %s' % str(request.__data__))
        return (await handler(request))
    return parse_data

async def response_factory(app, handler):
    async def response(request):
        logging.info('Response handler...')
        r = await handler(request)
        if isinstance(r, web.StreamResponse):
            return r
        if isinstance(r, bytes):
            resp = web.Response(body=r)
            resp.content_type = 'application/octet-stream'
            return resp
        if isinstance(r, str):
            if r.startswith('redirect:'):
                return web.HTTPFound(r[9:])
            resp = web.Response(body=r.encode('utf-8'))
            resp.content_type = 'text/html;charset=utf-8'
            return resp
        if isinstance(r, dict):
            template = r.get('__template__')
            if template is None:
                resp = web.Response(
                    body=json.dumps(
                        r,
                        ensure_ascii=False,
                        default=lambda o: o.__dict__).encode('utf-8'))
                resp.content_type = 'application/json;charset=utf-8'
                return resp
            else:
                resp = web.Response(
                    body=app['__templating__'].get_template(template).render(
                        **r).encode('utf-8'))
                resp.content_type = 'text/html;charset=utf-8'
                return resp
        if isinstance(r, int) and r >= 100 and r < 600:
            return web.Response(r)
        if isinstance(r, tuple) and len(r) == 2:
            t, m = r
            if isinstance(t, int) and t >= 100 and t < 600:
                return web.Response(t, str(m))
        # default:
        resp = web.Response(body=str(r).encode('utf-8'))
        resp.content_type = 'text/plain;charset=utf-8'
        return resp
    return response


def datetime_filter(t):
    delta = int(time.time() - t)
    if delta < 60:
        return u'1分钟前'
    if delta < 3600:
        return u'%s分钟前' % (delta // 60)
    if delta < 86400:
        return u'%s小时前' % (delta // 3600)
    if delta < 604800:
        return u'%s天前' % (delta // 86400)
    dt = datetime.fromtimestamp(t)
    return u'%s年%s月%s日' % (dt.year, dt.month, dt.day)

async def init(loop):
    await orm.create_pool(loop=loop, **configs.db)
    app = web.Application(loop=loop, middlewares=[
        logger_factory,auth_factory,response_factory
    ])
    init_jinja2(app, filters=dict(datetime=datetime_filter))
    add_routes(app, 'handlers')
    add_static(app)
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 9000)
    logging.info('服务启动于 http://127.0.0.1:9000...')
    return srv

loop = asyncio.get_event_loop()  # 创建协程
loop.run_until_complete(init(loop))  # 运行协程直到完成
loop.run_forever()  # 运行协程直到调用stop()
